#include <algorithm>
#include <cmath>
#include <cstdint>
#include <iostream>
#include <random>
#include <vector>

// Pixel class to encapsulate BGRA pixel data
class Pixel {
 public:
  uint8_t blue, green, red, alpha;

  Pixel() : blue(0), green(0), red(0), alpha(0) {}

  Pixel(uint8_t b, uint8_t g, uint8_t r, uint8_t a)
      : blue(b), green(g), red(r), alpha(a) {}

  // Color distance calculation for similarity detection
  double colorDistance(const Pixel& other) const {
    double db = (double)blue - other.blue;
    double dg = (double)green - other.green;
    double dr = (double)red - other.red;
    // Using Euclidean distance in RGB space
    return std::sqrt(db * db + dg * dg + dr * dr);
  }

  // Check if pixels are similar within tolerance
  bool isSimilar(const Pixel& other, double tolerance = 10.0) const {
    return colorDistance(other) <= tolerance;
  }

  // Convert to grayscale
  uint8_t toGrayscale() const {
    return (uint8_t)(0.299 * red + 0.587 * green + 0.114 * blue);
  }

  // Check if pixel is approximately white (for blank screen detection)
  bool isWhite(double tolerance = 20.0) const {
    Pixel white(255, 255, 255, alpha);
    return isSimilar(white, tolerance);
  }

  // Check if pixel is approximately black
  bool isBlack(double tolerance = 20.0) const {
    Pixel black(0, 0, 0, alpha);
    return isSimilar(black, tolerance);
  }
};

// PixelMap class for managing 2D pixel access
class PixelMap {
 private:
  uint8_t* buffer;
  int width, height, stride;

 public:
  PixelMap(uint8_t* buf, int w, int h, int s)
      : buffer(buf), width(w), height(h), stride(s) {}

  // Get pixel at coordinates (x, y)
  Pixel getPixel(int x, int y) const {
    if (x < 0 || x >= width || y < 0 || y >= height) {
      return Pixel();  // Return default pixel for out-of-bounds
    }

    uint8_t* row = buffer + (y * stride);
    uint8_t* pixel = row + (x * 4);

    return Pixel(pixel[0], pixel[1], pixel[2], pixel[3]);
  }

  // Set pixel at coordinates (x, y)
  void setPixel(int x, int y, const Pixel& p) {
    if (x < 0 || x >= width || y < 0 || y >= height) {
      return;  // Ignore out-of-bounds writes
    }

    uint8_t* row = buffer + (y * stride);
    uint8_t* pixel = row + (x * 4);

    pixel[0] = p.blue;
    pixel[1] = p.green;
    pixel[2] = p.red;
    pixel[3] = p.alpha;
  }

  int getWidth() const { return width; }
  int getHeight() const { return height; }
  int getTotalPixels() const { return width * height; }

  // Iterator class for random and sequential access
  class Iterator {
   private:
    const PixelMap* pixelMap;
    std::vector<std::pair<int, int>> positions;
    size_t currentIndex;

   public:
    // Constructor for sequential iteration
    Iterator(const PixelMap* pm, bool random = false, int sampleSize = -1)
        : pixelMap(pm), currentIndex(0) {
      if (sampleSize == -1) {
        sampleSize = pm->getTotalPixels();
      }

      // Generate all positions
      for (int y = 0; y < pm->height; y++) {
        for (int x = 0; x < pm->width; x++) {
          positions.push_back({x, y});
        }
      }

      if (random) {
        // Shuffle for random access
        std::random_device rd;
        std::mt19937 g(rd());
        std::shuffle(positions.begin(), positions.end(), g);

        // Limit to sample size
        if (sampleSize < (int)positions.size()) {
          positions.resize(sampleSize);
        }
      } else if (sampleSize < (int)positions.size()) {
        // For sequential sampling, take evenly distributed samples
        std::vector<std::pair<int, int>> sampled;
        int step = positions.size() / sampleSize;
        for (int i = 0; i < sampleSize; i++) {
          sampled.push_back(positions[i * step]);
        }
        positions = sampled;
      }
    }

    bool hasNext() const { return currentIndex < positions.size(); }

    std::pair<Pixel, std::pair<int, int>> next() {
      if (!hasNext()) {
        return {Pixel(), {-1, -1}};
      }

      auto pos = positions[currentIndex++];
      Pixel p = pixelMap->getPixel(pos.first, pos.second);
      return {p, pos};
    }

    void reset() { currentIndex = 0; }

    size_t size() const { return positions.size(); }
  };

  // Create iterator for sequential access
  Iterator createIterator() const { return Iterator(this, false); }

  // Create iterator for random access with optional sampling
  Iterator createRandomIterator(int sampleSize = -1) const {
    return Iterator(this, true, sampleSize);
  }
};

// Region detection statistics
struct RegionStats {
  Pixel dominantColor;
  double coverage;  // Percentage of image covered by this region
  int pixelCount;
  double avgColorDistance;  // Average distance from dominant color
  bool isBlankRegion;       // True if region appears to be a render error
};

// Feature extraction for render error detection
class RenderErrorDetector {
 private:
  double colorTolerance;
  double blankThreshold;  // Minimum coverage to consider as blank screen

 public:
  RenderErrorDetector(double tolerance = 15.0, double blankThresh = 0.8)
      : colorTolerance(tolerance), blankThreshold(blankThresh) {}

  // Detect large uniform regions using sampling
  std::vector<RegionStats> detectUniformRegions(const PixelMap& pixelMap,
                                                int sampleSize = 1000) {
    std::vector<RegionStats> regions;

    // Sample pixels for analysis
    auto iterator = pixelMap.createRandomIterator(sampleSize);
    std::vector<Pixel> samples;

    while (iterator.hasNext()) {
      auto [pixel, pos] = iterator.next();
      samples.push_back(pixel);
    }

    if (samples.empty()) return regions;

    // Simple clustering: check for dominant colors
    std::vector<std::vector<Pixel>> clusters;

    for (const auto& sample : samples) {
      bool foundCluster = false;

      for (auto& cluster : clusters) {
        if (!cluster.empty() && sample.isSimilar(cluster[0], colorTolerance)) {
          cluster.push_back(sample);
          foundCluster = true;
          break;
        }
      }

      if (!foundCluster) {
        clusters.push_back({sample});
      }
    }

    // Analyze clusters
    for (const auto& cluster : clusters) {
      if (cluster.empty()) continue;

      RegionStats stats;
      stats.pixelCount = cluster.size();
      stats.coverage = (double)cluster.size() / samples.size();

      // Calculate average color as dominant color
      uint64_t sumB = 0, sumG = 0, sumR = 0, sumA = 0;
      double totalDistance = 0;

      for (const auto& pixel : cluster) {
        sumB += pixel.blue;
        sumG += pixel.green;
        sumR += pixel.red;
        sumA += pixel.alpha;
      }

      stats.dominantColor = Pixel(sumB / cluster.size(), sumG / cluster.size(),
                                  sumR / cluster.size(), sumA / cluster.size());

      // Calculate average distance from dominant color
      for (const auto& pixel : cluster) {
        totalDistance += pixel.colorDistance(stats.dominantColor);
      }
      stats.avgColorDistance = totalDistance / cluster.size();

      // Check if this appears to be a blank region
      stats.isBlankRegion = (stats.coverage >= blankThreshold) &&
                            (stats.dominantColor.isWhite(colorTolerance) ||
                             stats.dominantColor.isBlack(colorTolerance));

      regions.push_back(stats);
    }

    // Sort by coverage (largest regions first)
    std::sort(regions.begin(), regions.end(),
              [](const RegionStats& a, const RegionStats& b) {
                return a.coverage > b.coverage;
              });

    return regions;
  }

  // Check if image appears to have render errors
  bool hasRenderError(const PixelMap& pixelMap) {
    auto regions = detectUniformRegions(pixelMap);

    for (const auto& region : regions) {
      if (region.isBlankRegion) {
        std::cout << "Detected potential render error: "
                  << (region.dominantColor.isWhite() ? "White" : "Black")
                  << " region covering " << (region.coverage * 100)
                  << "% of image" << std::endl;
        return true;
      }
    }

    return false;
  }
};

extern "C" {
int process_image(uint8_t* buffer, int width, int height, int stride) {
  // Create PixelMap for structured access
  PixelMap pixelMap(buffer, width, height, stride);

  // Statistics collection
  uint64_t sum_blue = 0, sum_green = 0, sum_red = 0, sum_alpha = 0;
  uint64_t total_pixels = 0;

  // Process all pixels using iterator
  auto iterator = pixelMap.createIterator();

  while (iterator.hasNext()) {
    auto [pixel, pos] = iterator.next();
    int x = pos.first;
    int y = pos.second;

    // Accumulate original values for statistics
    sum_blue += pixel.blue;
    sum_green += pixel.green;
    sum_red += pixel.red;
    sum_alpha += pixel.alpha;
    total_pixels++;

    // Convert to grayscale and update pixel
    uint8_t gray = pixel.toGrayscale();
    Pixel grayPixel(gray, gray, gray, pixel.alpha);
    pixelMap.setPixel(x, y, grayPixel);
  }

  // Calculate averages
  double avg_blue = (double)sum_blue / total_pixels;
  double avg_green = (double)sum_green / total_pixels;
  double avg_red = (double)sum_red / total_pixels;
  double avg_alpha = (double)sum_alpha / total_pixels;

  std::cout << "Processed " << total_pixels << " pixels" << std::endl;
  std::cout << "Average BGRA: (" << avg_blue << ", " << avg_green << ", "
            << avg_red << ", " << avg_alpha << ")" << std::endl;

  // Render error detection
  RenderErrorDetector detector;

  // Analyze regions for render errors
  auto regions = detector.detectUniformRegions(pixelMap);

  std::cout << "\nRegion Analysis:" << std::endl;
  for (size_t i = 0; i < std::min(regions.size(), (size_t)5); i++) {
    const auto& region = regions[i];
    std::cout << "Region " << (i + 1) << ": "
              << "Coverage=" << (region.coverage * 100) << "%, "
              << "Color=(" << (int)region.dominantColor.blue << ","
              << (int)region.dominantColor.green << ","
              << (int)region.dominantColor.red << "), "
              << "AvgDistance=" << region.avgColorDistance
              << (region.isBlankRegion ? " [POTENTIAL RENDER ERROR]" : "")
              << std::endl;
  }

  // Check for render errors
  bool hasError = detector.hasRenderError(pixelMap);
  if (hasError) {
    std::cout << "\n⚠️  RENDER ERROR DETECTED: Large uniform region suggests "
                 "rendering problem"
              << std::endl;
  } else {
    std::cout << "\n✅ No obvious render errors detected" << std::endl;
  }

  return hasError ? 1 : 0;  // Return 1 if render error detected, 0 otherwise
}
}
